package com.hotfix;

import android.app.Application;
import android.os.Build;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: sziitjin
 * @description:
 * @date: 2023/7/9
 */
public class FixUtil {
    public static void installPatch(Application application, File patch) {
        Log.i("FixUtil", "installPatch");
        if (!patch.exists()) {
            Log.e("FixUtil", "installPatch patch is not exists.");
            return;
        }
        Log.i("FixUtil", "installPatch patch " + patch.length());
        List<File> patchFile = new ArrayList<>();
        patchFile.add(patch);
        // 1、获取程序的PathClassLoader对象
        ClassLoader classLoader = application.getClassLoader();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            try {
                ClassLoaderInjector.inject(application, classLoader, patchFile);
            } catch (Throwable throwable) {
            }
            return;
        }
        try {
            // 2、反射获得PathClassLoader父类BaseDexClassLoader的pathList对象
            Field pathListField = ShareReflectUtil.findField(classLoader, "pathList");
            Object pathList = pathListField.get(classLoader);
            // 3、反射获取pathList的dexElements对象 （oldElement）
            Field dexElementsField = ShareReflectUtil.findField(pathList, "dexElements");
            Object[] oldElements = (Object[]) dexElementsField.get(pathList);
            // 4、把补丁包变成Element数组：patchElements（反射执行makePathElements）
            Object[] patchElements = null;
            ArrayList<IOException> ioExceptions = new ArrayList<>();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                Method makePathElementsMethod = ShareReflectUtil.findMethod(pathList, "makePathElements",
                        List.class, File.class, List.class);
                patchElements = (Object[]) makePathElementsMethod.invoke(pathList, patchFile,
                        application.getCacheDir(), ioExceptions);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                Method makePathElements = ShareReflectUtil.findMethod(pathList, "makeDexElements",
                        ArrayList.class, File.class, ArrayList.class);
                patchElements = (Object[])
                        makePathElements.invoke(pathList, patchFile, application.getCacheDir(), ioExceptions);
            }
            // 5、合并patchElement+oldElement = newElement （Array.newInstance）
            Object[] newElements = (Object[]) Array.newInstance(oldElements.getClass().getComponentType(),
                    oldElements.length + patchElements.length);
            Log.i("FixUtil", "installPatch patchElements " + patchElements.length + ",oldElements " + oldElements.length);
            // copy patchElements
            System.arraycopy(patchElements, 0, newElements, 0, patchElements.length);
            // copy oldElements
            System.arraycopy(oldElements, 0, newElements, patchElements.length, oldElements.length);
            // 6、反射把oldElement赋值成newElement
            dexElementsField.set(pathList, newElements);
            Log.i("FixUtil", "installPatch end");
        } catch (Exception e) {
            Log.i("FixUtil", "err:" + e.getMessage());
        }

    }
}
